﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Xml.Serialization;
using System.IO;
using System.Xml;

namespace Nizm0.QOT.Library.Objects
{
    [Serializable]
    [XmlRoot("ProcedureHistory")]
    [XmlInclude(typeof(ProcedureVersion))]
    public class ProcedureHistory : IProcedure
    {
        public ProcedureHistory()
        { }

        //public ProcedureHistory(Objects.IProcedureInfo proc)
        //{
        //    this.Name = proc.Name;
        //}

        public ProcedureHistory(Objects.IProcedure procedure, string storagelocation)
        {
            string locationForProcedure = string.Format("{0}\\{1}\\", storagelocation, procedure.Name);
            if (!Directory.Exists(locationForProcedure))
                Directory.CreateDirectory(locationForProcedure);
            this.Folder = locationForProcedure;

            this._actionInProcedure = procedure.ActionInProcedure;
            this._hash = procedure.Hash;
            this._listOfVariable = procedure.ListOfVariable;
            this._type = procedure.Type;
            this.SourceType = procedure.SourceType;
            this.Name = procedure.Name;
            this.CreationTime = procedure.CreationTime;
            this.ModifiedTime = procedure.ModifiedTime;
            this.Version = procedure.Version;


            if (File.Exists(this.Folder + "versions.xml"))
            {
                using (StreamReader strr = new StreamReader(this.Folder + "versions.xml"))
                {
                    ReadXml(XmlReader.Create(strr));
                }
            }
            else
            {
                using (StreamWriter strw = new StreamWriter(this.Folder + "versions.xml"))
                {
                    strw.Write(Serialize());
                    strw.Close();
                }
            }

        }

        public static event ErrorEventHandler ErrorEvent;
        public static event ChangeStatusEvetHendler StatusChange;
        public event VersionChangeEventHandler VersionChange;

        public void OnVersionChange(object sender, VersionChangeEventArgs e)
        {
            if (VersionChange != null)
                VersionChange(sender, e);
        }

        private IProcedureInfo _parent;

        [XmlIgnore]
        private Procedure.EnumActionInProcedure _actionInProcedure;

        [XmlAttribute("ActionInProcedure")]
        public Procedure.EnumActionInProcedure ActionInProcedure
        {
            get { return _actionInProcedure; }
            set { _actionInProcedure = value; }
        }

        [XmlAttribute("CreationTime")]
        public DateTime CreationTime
        {
            get;
            set;
        }

        private bool _exported = false;
        [XmlAttribute("Exported")]
        public bool Exported
        {
            get { return _exported; }
            set { _exported = value; }
        }

        [XmlAttribute("Folder")]
        public string Folder;

        protected string _hash = string.Empty;

        [XmlAttribute("Hash")]
        public string Hash
        {
            get { return _hash; }
        }

        [XmlIgnore]
        private List<VariableInTheProcedure> _listOfVariable = new List<VariableInTheProcedure>();

        [XmlIgnore]
        public List<VariableInTheProcedure> ListOfVariable
        {
            get
            {
                return _listOfVariable;
            }
            set
            {
                _listOfVariable = value;
            }
        }

        private List<IProcedure> _procedureList = new List<IProcedure>();

        private bool _loadedFromSettings = false;

        [XmlIgnore]
        public bool LoadedFromSettings
        {
            get
            {
                return _loadedFromSettings;
            }
            set
            {
                _loadedFromSettings = value;
            }
        }

        [XmlAttribute("ModifiedTime")]
        public DateTime ModifiedTime
        {
            get;
            set;
        }

        [XmlAttribute("Name")]
        public string Name { get; set; }

        [XmlAttribute("Source")]
        public string Source { get; set; }

        [XmlAttribute("SourceType")]
        public Procedure.EnumSource SourceType
        {
            get;
            set;
        }

        [XmlIgnore()]
        public string Text
        {
            get;
            set;
        }

        [XmlAttribute("Location")]
        public string Location
        {
            get;
            set;
        }

        [NonSerialized]
        [XmlIgnore]
        private Procedure.EnumProcType _type;
        [XmlAttribute("Type")]
        public Procedure.EnumProcType Type
        {
            get;
            set;
        }

        private ProcedureVersion _version;
        [XmlElement("Version")]
        public ProcedureVersion Version
        {
            get
            {
                if (_procVersionList != null && _procVersionList.Count > 0)
                {
                    _version = _procVersionList[0];
                    _version.Parent = this;
                    _version.Hash = this.Hash;
                    return _version;
                }
                else
                    return null;
            }
            set
            {
                OnVersionChange(this, new VersionChangeEventArgs(_version, value));
                value.Parent = this;
                _version = value;
                if (!ProcVersionList.Exists(v => v.Version == value.Version))
                    ProcVersionList.Add(value);
                else
                {
                    ProcVersionList.RemoveAll(v => v.Version == value.Version);
                    ProcVersionList.Add(value);
                }
            }
        }

        private List<ProcedureVersion> _procVersionList = new List<ProcedureVersion>();
        [XmlIgnore]
        public List<ProcedureVersion> ProcVersionList
        {
            get
            {
                if(_procVersionList.Count<=0)
                    if (File.Exists(this.Folder + "versions.xml"))
                    {
                        using (StreamReader strr = new StreamReader(this.Folder + "versions.xml"))
                        {
                            ReadXml(XmlReader.Create(strr));
                        }
                    }
                return _procVersionList;
            }
            set { _procVersionList = value; }
        }

        public IProcedureInfo Parent
        {
            get { return _parent; }
            set { _parent = value; }
        }

        public static void SetErrorEvent(ErrorEventHandler test)
        {
            if (ErrorEvent == null)
                ErrorEvent += test;
        }

        public static void SetStatusChangeEvent(ChangeStatusEvetHendler test)
        {
            if (StatusChange == null)
                StatusChange += test;
        }

        public LocalStorage.StorageResponse AddNewVersion(IProcedure proc)
        {
            if (string.IsNullOrWhiteSpace(this.Folder))
                return LocalStorage.StorageResponse.Error;
            else
                return AddNewVersion(proc, this.Folder);
        }

        public LocalStorage.StorageResponse AddNewVersion(IProcedure proc, string folder)
        {
            this.Folder = folder;
            LocalStorage.StorageResponse result = LocalStorage.StorageResponse.NotExists;
            if (_procVersionList.Exists(v => v.Version == proc.Version.Version))
            {
                return UpdateVersion(proc);
            }
            else
            {
                _procedureList.Add(proc);
                _procVersionList.Add(proc.Version);
                _procVersionList.Sort(delegate(ProcedureVersion pv1, ProcedureVersion pv2)
                {
                    lock (this)
                    {
                        if (pv1.Version == ProcedureVersion.Unknown || pv2.Version == ProcedureVersion.Unknown)
                            return pv1.Date.CompareTo(pv2.Date);
                        //return pv1.Version.CompareTo(pv2.Version);
                        return pv1.CompareTo(pv2);
                    }
                });


                result = LocalStorage.StorageResponse.NewWersion;
            }
            SerializeVersionList();
            return result;
        }

        public bool ChangeVersion(object sender, Library.VersionChangeEventArgs e)
        {
            return true;
        }

        private void SerializeVersionList()
        {
            using (StreamWriter strwr = new StreamWriter(this.Folder + "versions.xml", false))
            {
                try
                {
                    var settings = new XmlWriterSettings
                    {
                        Indent = true
                    };

                    var xml = new StringBuilder();
                    using (var writer = XmlWriter.Create(strwr, settings))
                    {
                        writer.WriteStartElement("Versions");
                        foreach (Library.Objects.ProcedureVersion proc in _procVersionList)
                        {
                            var nsSerializer = new XmlSerializerNamespaces();
                            nsSerializer.Add("", "");

                            //var xmlSerializer = new XmlSerializer(typeof(Library.Objects.Procedure), "");
                            System.Xml.Serialization.XmlSerializer xmlSerializer = new System.Xml.Serialization.XmlSerializer(proc.GetType(), "");
                            xmlSerializer.Serialize(writer, proc, nsSerializer);
                        }
                        writer.WriteEndElement();
                    }
                    strwr.Close();
                }
                catch
                {
                    strwr.Close();
                }
            }
        }

        public void ReadXml(System.Xml.XmlReader reader)
        {
            reader.MoveToContent();

            XmlDocument doc = new XmlDocument();
            doc.Load(reader);
            XmlNode node = doc.ChildNodes[0];
            //"LocalStorage/ProcedureList"
            foreach (XmlNode tmpnode in node.SelectNodes("//ProcedureVersion"))
            {
                String className = tmpnode.Name.Trim(); //read the class name 
                //use the namespace IDelivery is located in
                className = "Nizm0.QOT.Library.Objects." + className;

                System.Type classType = System.Type.GetType(className);
                System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(System.Type.GetType(className));
                XmlReader r = new XmlNodeReader(tmpnode);
                var vers = (Library.Objects.ProcedureVersion)Convert.ChangeType(serializer.Deserialize(r), classType);
                vers.Parent = this;
                _procVersionList.Add(vers);
            }
        }

        public LocalStorage.StorageResponse UpdateVersion(IProcedure proc)
        {
            if (_procedureList.Exists(p => p.Hash == proc.Hash))
            {
                return LocalStorage.StorageResponse.NewWersion;
            }
            else
            {
                _procedureList.Add(proc);
                return LocalStorage.StorageResponse.NotExists;
            }
        }

        public virtual T Deserialize<T>(string xmlobject)
                    where T : class
        {
            byte[] byteArray = Encoding.ASCII.GetBytes(xmlobject);
            MemoryStream stream = new MemoryStream(byteArray);
            System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(typeof(T));
            T ip = (T)x.Deserialize(stream);
            return ip;
        }

        public void Marge(Procedure procedure)
        {
            throw new NotImplementedException();
        }

        public virtual string Serialize()
        {
            using (Stream str = new MemoryStream())
            {
                //System.Runtime.Serialization.Formatters.Binary.BinaryFormatter t = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(
                System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(typeof(ProcedureHistory));
                x.Serialize(str, this);
                using (StreamReader stmr = new StreamReader(str))
                {
                    return stmr.ReadToEnd();
                }
            }
        }

        public System.Windows.Forms.UserControl Show()
        {
            throw new NotImplementedException();
        }

        public void ShowText()
        {
            throw new NotImplementedException();
        }

        public override string ToString()
        {
            return this.Name;
        }


        public void ChangeVersion(ProcedureVersion ProcVer)
        {
            this.Version.Version = ProcVer.Version;
            this.Version.Date = ProcVer.Date;
            this.Version.Comment = ProcVer.Comment;
            this.Version.Hash = ProcVer.Hash;
            this.Version.Project = ProcVer.Project;
            this.Version.User = ProcVer.User;
        }
    }
}